// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#include <climits>
#include <iostream>

#include "Platform/System.h"

#include "Rules/ImportRule.h"

#include "FileImportInfo.h"

using namespace CangjieMigrator;
using namespace Platform;

bool FileImportInfo::Result::IsValid() const
{
    if (beginLine > endLine) {
        return false;
    }
    if (beginLine < 1) {
        return false;
    }
    return true;
}

std::string FileImportInfo::Result::GenerateString() const
{
    std::string result = "";
    for (const auto &line : lines) {
        result += line + System::NewLine;
    }
    return result;
}

// 默认构造函数
FileImportInfo::FileImportInfo() : beginLine(INT_MAX), endLine(-1), needImportStdx(false)
{}

// 移动构造函数
FileImportInfo::FileImportInfo(FileImportInfo &&other) noexcept
    : infos(std::move(other.infos)), beginLine(other.beginLine), endLine(other.endLine),
      needImportStdx(other.needImportStdx)
{}

FileImportInfo &FileImportInfo::operator=(FileImportInfo &&) noexcept = default;

void FileImportInfo::Add(const Cangjie::AST::PackageSpec &node, const PositionRange &range)
{
    packageSpecLocation = std::make_unique<PositionRange>(range);
}

void FileImportInfo::Add(const Cangjie::AST::ImportSpec &node, const PositionRange &range)
{
    for (const auto &info : infos) {
        if (info->positionRange.IsInclude(range)) {
            return;
        }
    }
    auto info = ImportInfo::Build(node, range);
    if (!info) {
        return;
    }
    if (info->IsImplicit()) {
        return;
    }

    beginLine = std::min(beginLine, range.start.line);
    endLine = std::max(endLine, range.end.line);
    infos.emplace_back(std::move(info));
}

void FileImportInfo::Add(std::unique_ptr<ImportInfo> info)
{
    infos.emplace_back(std::move(info));
}

std::vector<PackageExpression> FileImportInfo::GetImports() const
{
    auto imports = std::vector<PackageExpression>();
    for (const auto &info : infos) {
        info->ExportTo(imports);
    }
    return imports;
}

void FileImportInfo::DirectReplaceChange()
{
    for (const auto &info : infos) {
        info->TryReplacePackage("std.os.process", "std.process");
        info->TryReplacePackage("std.os.posix", "std.posix");
        info->TryReplacePackage("std.socket", "std.net");
        info->TryReplacePackage("std.format", "std.convert");
        info->TryReplacePackage("std.console", "std.env");
        needImportStdx |= info->TryReplacePackage("compress", "stdx.compress");
        needImportStdx |= info->TryReplacePackage("crypto", "stdx.crypto");
        needImportStdx |= info->TryReplacePackage("encoding", "stdx.encoding");
        needImportStdx |= info->TryReplacePackage("fuzz", "stdx.fuzz");
        needImportStdx |= info->TryReplacePackage("log", "stdx.log");
        needImportStdx |= info->TryReplacePackage("net", "stdx.net");
        needImportStdx |= info->TryReplacePackage("serialization", "stdx.serialization");
    }
}

void FileImportInfo::Change(const std::set<const Rules::ImportRule *> &matchRules)
{
    // Try to modify and remove the import statement that is empty after modification
    for (const auto rule : matchRules) {
        auto newEnd = std::remove_if(infos.begin(), infos.end(), [&rule](const std::unique_ptr<ImportInfo> &info) {
            return info->TryModifyOrRemove(*rule);
        });
        infos.erase(newEnd, infos.end());
    }

    // Add missing import statements
    for (const auto &rule : matchRules) {
        for (auto &e : rule->GetAdd()) {
            auto isIncluded = false;
            for (auto &i : GetImports()) {
                if (i.IsInclude(e)) {
                    isIncluded = true;
                }
            }
            if (!isIncluded) {
                infos.emplace_back(std::move(std::make_unique<ImportIdentifierInfo>(e)));
            }
        }
    }
}

bool FileImportInfo::NeedImportStdx() const
{
    return needImportStdx;
}

FileImportInfo::Result FileImportInfo::Generate() const
{
    Result result;
    if (endLine < beginLine || beginLine <= 0) { // There's no import spec.
        if (packageSpecLocation) {
            result.beginLine = packageSpecLocation->end.line + 1;
        } else {
            result.beginLine = 1;
        }
        result.endLine = result.beginLine;
    } else {
        result.beginLine = beginLine;
        result.endLine = endLine + 1;
    }
    for (const auto &info : infos) {
        result.lines.emplace_back(std::move(info->ToString()));
    }
    return result;
}
